from pygame.sprite import Group, RenderUpdates
from gamesprite import *
from minisprite import ThroughSprite
from driver import CollisionSignal
from ai import GameAI
from blackboard import BlackBoard
from tools import *
from level import *

class LayerGroup(object):
    """ provide layer manage group """
    def __init__(self):
        self.__g = {}
        
    def add(self, spr):
        if not self.__g.has_key(spr.layer):
            self.__g[spr.layer] = RenderUpdates()
        self.__g[spr.layer].add(spr) 
    
    def draw(self, sf):
        layer = self.__g.keys()
        layer.sort() # orderly
        for ly in layer:
            self.__g[ly].draw(sf)
    
    def remove(self, spr):
        if self.__g.has_key(spr.layer):
            self.__g[spr.layer].remove(spr) 

class KindGroup(object):
    """ provide kind manage group """
    def __init__(self):
        self.__g = {}
        
    def add(self, spr):
        if not self.__g.has_key(spr.__class__):
            self.__g[spr.__class__] = Group()
        self.__g[spr.__class__].add(spr)
        
    def sprites(self, kindlist):
        spr = []
        for kind in kindlist: # 
            if self.__g.has_key(kind):
                spr += self.__g[kind].sprites()
        return spr

class DataMap(object):
    """ analyze level config and manage ref map """ 
    SPRITES = { # type : class
               0 : Plane ,
               1 : Player,
               2 : EnemyFactory,
               3 : Brick,
               4 : Base,
               5 : Steel,
               6 : Lawn,
               7 : River,
               8 : Ice,
               9 : BossFactory, 
               }
    GRID = 16
    XAXIS = 40
    YAXIS = 40
    def __init__(self, rawmap): # rawmap = (item1, item2...itemn)
        super(DataMap, self).__init__()
        self.sprgroup = KindGroup()
        self.updates = Group()
        self.render = LayerGroup()
        self.board = BlackBoard()
        self.ai = GameAI # ai machine
        self.__refmap = []
        for y in xrange(DataMap.YAXIS): # init reference map
            self.__refmap.append([])
            for x in xrange(DataMap.XAXIS):
                self.__refmap[y].append([])
        # create sprite
        for item in rawmap:
            spr = DataMap.SPRITES[item[0]](item[1], self, *item[2])
            self.sprgroup.add(spr)
            if spr.logical:
                self.updates.add(spr)
            if spr.visible: 
                self.render.add(spr)
                self.ref_add(spr, spr.node)
        self.player = self.sprgroup.sprites((Player, ))[0]
        self.plane = self.sprgroup.sprites((Plane, ))[0]

    def ref_delete(self, spr):
        """ delete a sprite from ref map """
        # out map bound
        for hold in spr.hold:
            if spr.node[0] + hold[0] < 0 \
            or spr.node[0] + hold[0] >= DataMap.XAXIS \
            or spr.node[1] + hold[1] < 0 \
            or spr.node[1] + hold[1] >= DataMap.YAXIS:
                return
        # remove it
        for hold in spr.hold:
            if spr in self.__refmap[spr.node[1] + hold[1]][spr.node[0] + hold[0]]:
                self.__refmap[spr.node[1] + hold[1]][spr.node[0] + hold[0]].remove(spr)

    def ref_add(self, spr, node):
        """ add a sprite to ref map """
        # collision test
        collision = {}
        # test bound
        for hold in spr.hold:
            if node[0] + hold[0] < 0 \
            or node[0] + hold[0] >= DataMap.XAXIS \
            or node[1] + hold[1] < 0 \
            or node[1] + hold[1] >= DataMap.YAXIS:
                collision[None] = True
        if not collision: # not collide map bound
            for hold in spr.hold:
                for collspr in self.__refmap[node[1] + hold[1]][node[0] + hold[0]]: # find all this node ref list
                    # two spr has no tru
                    if not collspr.attr.has_key(ThroughSprite.ATTRNAME) \
                    and not spr.attr.has_key(ThroughSprite.ATTRNAME):
                        collision[collspr] = True
                    # one have
                    elif collspr.attr.has_key(ThroughSprite.ATTRNAME) \
                    and not spr.attr.has_key(ThroughSprite.ATTRNAME) \
                    and not collspr.tru_passable(spr): # if has tru attribute and not allow this spr 
                        collision[collspr] = True
                    # another have
                    elif not collspr.attr.has_key(ThroughSprite.ATTRNAME) \
                    and spr.attr.has_key(ThroughSprite.ATTRNAME) \
                    and not spr.tru_passable(collspr): # if has tru attribute and not allow this spr 
                        collision[collspr] = True
                    # both have
                    elif collspr.attr.has_key(ThroughSprite.ATTRNAME) \
                    and spr.attr.has_key(ThroughSprite.ATTRNAME) \
                    and not collspr.tru_passable(spr) \
                    and not spr.tru_passable(collspr): # if has tru attribute and not allow this spr 
                        collision[collspr] = True
        # not collide, add to new position
        if not collision:
            spr.node = node
            for hold in spr.hold:
                if spr not in self.__refmap[node[1] + hold[1]][node[0] + hold[0]]:
                    self.__refmap[node[1] + hold[1]][node[0] + hold[0]].append(spr)
            return False
        else: # send collision signal
            for collspr in collision.keys():
                if collspr == None:
                    spr.driver.send(CollisionSignal())
                else:
                    spr.driver.send(CollisionSignal(collspr))
                    collspr.driver.send(CollisionSignal(spr))
            return True    

    def ref_change(self, spr, desnode):
        """ delete old position and add to new position """
        self.ref_delete(spr)
        collision = self.ref_add(spr, desnode)
        if collision: self.ref_add(spr, spr.node) # if collided, add to source node
        return collision

    def get_simplemap(self, weightdict):
        """ provide simple, configurable datamap """
        sm = []
        for x in xrange(DataMap.XAXIS):
            sm.append([0] * DataMap.YAXIS)
        sprcls = weightdict.keys()
        for spr in self.sprgroup.sprites(sprcls):
            for hold in spr.hold:
                sm[spr.node[1] + hold[1]][spr.node[0] + hold[0]] \
                = weightdict[spr.__class__]
        return sm

    def get_state(self):
        """ get all game state """
        player = (self.player.vtl_state(), 
                  -self.player.power,
                  self.player.kills,
                  self.player.score,
                  self.player.killboss)
        plane = (self.plane.vtl_state(), 
                 -self.plane.power,
                 self.plane.kills,
                 self.plane.score,
                 self.plane.killboss)
        enemy = []
        alive = len(self.sprgroup.sprites((Enemy, )))
        bf = self.sprgroup.sprites((BossFactory, ))
        be = self.sprgroup.sprites((BossEnemy, ))
        if bf: bf = bf[0].state()
        else: bf = 0
        if be: be = be[0].vtl_state()
        else: be = 0
        boss = (bf, be)
        for factory in self.sprgroup.sprites((EnemyFactory, )):
            enemy.append(factory.state())
        return player, plane, enemy, alive, boss




















